import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { RegisterDto } from './dto/register.dto';
import * as bcrypt from 'bcrypt';
import { UserService } from '../user/user.service';
import { User } from 'src/entities/user.entity';
import { LoginDto } from './dto/login.dto';
import { RefreshTokenDto } from './dto/refresh.dto';
import { BusinessException } from 'src/common/exceptions/business.exception';
import { ErrorCode } from 'src/common/constants/error-code.enum';

@Injectable()
export class AuthService {
  constructor(
    private readonly userService: UserService,
    private readonly jwtService: JwtService,
  ) {}

  /**
   * 用户注册
   * 1. 检查用户名是否已存在
   * 2. 检查邮箱是否已存在
   * 3. 加密密码
   * 4. 创建用户
   * 5. 生成Token
   * 6. 返回用户信息和Token
   */
  async register(registerDto: RegisterDto) {
    const { username, email, password } = registerDto;
    // 1. 检查用户名是否已存在
    const existingUsername = await this.userService.findByUsername(username);
    if (existingUsername) {
      throw new BusinessException(ErrorCode.USER_ALREADY_EXISTS, '用户名已存在');
    }

    // 2. 检查邮箱是否已存在
    const existingEmail = await this.userService.findByEmail(email);
    if (existingEmail) {
      throw new BusinessException(ErrorCode.EMAIL_ALREADY_EXISTS);
    }

    // 3. 加密密码
    const salt = await bcrypt.genSalt(10);
    const hashedPassword = await bcrypt.hash(password, salt);

    // 4. 创建用户
    const user = await this.userService.create({
      username,
      email,
      password: hashedPassword,
    });

    // 5. 生成Token
    const tokens = await this.generateTokens(user);

    // 6. 返回用户信息和Token（移除密码字段）
    const { password: _password, ...userInfo } = user;
    return {
      user: userInfo,
      ...tokens,
    };
  }

  /**
   * 用户登录
   *
   * 1. 检查邮箱是否已存在
   * 2. 检查密码是否正确
   * 3. 生成Token
   * 4. 返回用户信息和Token
   * 5. 更新最后登录信息
   */
  async login(loginDto: LoginDto, ip: string) {
    const { identifier, password } = loginDto;

    // 1. 查找用户
    const user = await this.userService.findByIdentifier(identifier);

    if (!user) {
      throw new BusinessException(ErrorCode.INVALID_CREDENTIALS);
    }

    // 2. 验证密码
    const isPasswordValid = await bcrypt.compare(password, user.password);
    if (!isPasswordValid) {
      throw new BusinessException(ErrorCode.INVALID_CREDENTIALS);
    }

    // 3. 检查账号状态
    if (user.status === 'banned') {
      throw new BusinessException(ErrorCode.USER_DISABLED);
    }

    if (user.status === 'inactive') {
      throw new BusinessException(
        ErrorCode.USER_DISABLED,
        '账号未激活，请先激活账号',
      );
    }

    // 4. 更新最后登录信息
    await this.userService.updateUser(user.id, {
      lastLoginAt: new Date(),
      lastLoginIp: ip,
    });

    // 5. 生成Token
    const tokens = await this.generateTokens(user);

    // 6. 返回用户信息和Token（移除密码字段）
    const { password: _password, ...userInfo } = user;
    return {
      user: userInfo,
      ...tokens,
    };
  }

  /**
   * 刷新Access Token
   *
   * 使用Refresh Token换取新的Access Token
   */
  async refreshToken(refreshTokenDto: RefreshTokenDto) {
    const { refreshToken } = refreshTokenDto;

    try {
      // 1. 验证Refresh Token
      const payload = this.jwtService.verify(refreshToken);

      // 2. 查找用户
      const user = await this.userService.findById(payload.sub);
      if (!user) {
        throw new BusinessException(ErrorCode.USER_NOT_FOUND);
      }

      // 3. 检查账号状态
      if (user.status !== 'active') {
        throw new BusinessException(ErrorCode.USER_DISABLED);
      }

      // 4. 生成新的Token
      const tokens = await this.generateTokens(user);

      return tokens;
    } catch (error) {
      throw new BusinessException(ErrorCode.INVALID_TOKEN);
    }
  }

  /**
   * 生成Access Token和Refresh Token
   */
  private async generateTokens(user: User) {
    const payload = {
      sub: user.id,
      username: user.username,
      email: user.email,
      membershipLevel: user.membershipLevel,
    };

    // Access Token（15分钟）
    const accessToken = this.jwtService.sign(payload, {
      expiresIn: '15m',
    });

    // Refresh Token（7天）
    const refreshToken = this.jwtService.sign(payload, {
      expiresIn: '7d',
    });

    return {
      accessToken,
      refreshToken,
      expiresIn: 15 * 60, // 15分钟（秒）
    };
  }
}
